package com.monitor.methods;

import java.io.IOException;
import java.net.InetAddress;
import java.util.Arrays;

import jpcap.JpcapCaptor;
import jpcap.JpcapSender;
import jpcap.NetworkInterface;
import jpcap.NetworkInterfaceAddress;
import jpcap.packet.ARPPacket;
import jpcap.packet.EthernetPacket;
import jpcap.packet.IPPacket;
import jpcap.packet.TCPPacket;

public class Network {

	NetworkInterface[] devices = null;

	/*
	 * Our constructor that gets the list of NetworkInterfaces and populates the
	 * devices array
	 */

	public Network() {
		devices = JpcapCaptor.getDeviceList();
	}

	public NetworkInterface[] getNetowrkInterfaces() {
		return devices;
	}

	public byte[] myMacAddress(int networkInterfaceID) {
		/* Verify that we obtained a valid networkInterfaceID */

		if (networkInterfaceID < 0 || devices.length <= networkInterfaceID)
			return null;

		/*
		 * Set device to the network interface specified by the
		 * networkInterfaceID
		 */

		NetworkInterface device = devices[networkInterfaceID];

		/* Get the MAC address of the device */

		byte[] returnByte = device.mac_address;

		/*
		 * Convert the MAC address to a string so we can verify we have a valid
		 * MAC address
		 */

		String macString = "";
		for (byte b : returnByte)
			macString += Integer.toHexString(b & 0xff) + ":";

		/*
		 * This function is to fix a problem with the windows jpcap and
		 * obtaining a bad MAC address
		 */

		if (macString.equalsIgnoreCase("0:0:0:0:0:0:"))
			returnByte = myMacAddress(0);
		return returnByte;
	}

	public byte[] myIPAddress(int networkInterfaceID) {

		/* Verify that we obtained a valid networkInterfaceID */
		if (networkInterfaceID < 0 || devices.length <= networkInterfaceID)
			return null;

		/*
		 * Set device to the network interface specified by the
		 * networkInterfaceID
		 */

		NetworkInterface device = devices[networkInterfaceID];

		/*
		 * Get the array of IP addresses assigned to the network interface
		 * specified by the networkInterfaceID
		 */

		NetworkInterfaceAddress[] addresses = devices[networkInterfaceID].addresses;

		/*
		 * Network interfaces can have multiple IP addresses assigned to them
		 * but in this example we are only grabbing the first address.
		 */

		InetAddress address = addresses[0].address;

		/*
		 * Convert the IP address to an array of bytes and return it to the
		 * method that called this function
		 */

		byte[] returnByte = address.getAddress();
		return returnByte;
	}

	public boolean ACKheartBeat(int networkInterfaceID, String ipaddress,
			int srcPort, int dstPort) {
		/* set our return value to false */

		boolean booleanret = false;

		/*
		 * Lets obtain our MAC address and put it in an array of bytes called
		 * myMac
		 */
		byte[] myMac = myMacAddress(networkInterfaceID);

		/*
		 * Lets obtain our IP address and put it in an array of bytes called
		 * myIPAdd
		 */

		byte[] myIPAdd = myIPAddress(networkInterfaceID);

		/*
		 * Set device to the network interface specified by the
		 * networkInterfaceID
		 */

		NetworkInterface device = devices[networkInterfaceID];
		try {

			/*
			 * Initialize our network device and open it for sending/receiving
			 */
			JpcapCaptor captor = JpcapCaptor.openDevice(device, 2000, false, 5000);
			String filter = "tcp dst port " + srcPort;
			captor.setFilter(filter, true);

			/*
			 * convert the remote machines IP address to an array of bytes
			 * called recAddr
			 */

			InetAddress[] recAddr0 = InetAddress.getAllByName(ipaddress);
			byte[] recAddr = recAddr0[0].getAddress();

			/*
			 * get the MAC address of the remote machine using the arp method
			 */

			byte[] recMac = arp(InetAddress.getByAddress(recAddr),
					InetAddress.getByAddress(myIPAdd), networkInterfaceID);

			/* initiate our sender instance of the network device */

			JpcapSender sender = JpcapSender.openDevice(device);

			/* Initialize a new instance of TCPPacket called p */

			TCPPacket p = new TCPPacket(srcPort, dstPort, 60, 60, false, true,
					false, false, false, false, false, false, 65535, 0);

			/* Set the IP version 4 parameters of the TCPPacket */

			p.setIPv4Parameter(0, false, false, false, 0, false, false, false,
					0, 0, 200, IPPacket.IPPROTO_TCP,
					InetAddress.getByAddress(myIPAdd),
					InetAddress.getByAddress(recAddr));

			/* Set the data to send with the connection */

			p.data = "".getBytes();

			/*
			 * Initiate the EthernetPacke ether that will transport the TCP
			 * packet and start to build the packet
			 */

			EthernetPacket ether = new EthernetPacket();
			ether.frametype = EthernetPacket.ETHERTYPE_IP;
			// set the Ethernet Frame type to an IP request
			ether.src_mac = myMac; // Set the source MAC address to
									// our MAC address
			ether.dst_mac = recMac; // Set the destination MAC address
									// to the remote MAC add
			p.datalink = ether; // Set the Data Link Layer of the
								// IPpacket to the Ethernet packet

			/* send out the TCP packet */

			sender.sendPacket(p);

			/*
			 * Captor the packets coming back based on the filter that was set
			 * with the setFilter method
			 */

			TCPPacket returnPacket = (TCPPacket) captor.getPacket();

			/* If the packet captor times out set the return value to false */
			if (returnPacket == null)
				booleanret = false;
			else {

				/*
				 * If we received a return packet from the remote machine set
				 * the return value to true
				 */

				System.out.println("received from: " + returnPacket.src_ip);
				System.out.println("RST: " + returnPacket.rst);
				booleanret = true;
			}
		} catch (Exception e) {
			System.out.println(e);
			return (false);
		}
		return booleanret;
	}

	public byte[] arp(InetAddress ip, InetAddress srcip, int networkInterfaceID) throws IOException {

		/* Verify that we obtained a valid networkInterfaceID */

		if (networkInterfaceID < 0 || devices.length <= networkInterfaceID)
			return null;

		/*
		 * Set device to the network interface specified by the
		 * networkInterfaceID
		 */

		NetworkInterface device = devices[networkInterfaceID];

		/*
		 * Lets obtain our MAC address and put it in an array of bytes called
		 * myMac
		 */

		byte[] myMac = myMacAddress(networkInterfaceID);

		/* Initialize our network device and open it for sending/receiving */

		JpcapCaptor captor = JpcapCaptor.openDevice(device, 2000, false, 3000);
		captor.setFilter("arp", true);

		/* initiate our sender instance of the network device */

		JpcapSender sender = captor.getJpcapSenderInstance();

		/*
		 * We will use ff:ff:ff:ff:ff:ff as our broadcast address so everyone on
		 * our network will receive the ARP request
		 */

		byte[] broadcast = new byte[] { (byte) 255, (byte) 255, (byte) 255,
				(byte) 255, (byte) 255, (byte) 255 };

		/* Initiate an ARPPacket called arp and build the packet */

		ARPPacket arp = new ARPPacket();
		arp.hardtype = ARPPacket.HARDTYPE_ETHER; // set hardware type to
													// Ethernet
		arp.prototype = ARPPacket.PROTOTYPE_IP; // set the protocol type to IP
		arp.operation = ARPPacket.ARP_REQUEST; // define the operation to be an
												// ARP request
		arp.hlen = 6; // Hardware address length
		arp.plen = 4; // protocol address length
		arp.sender_hardaddr = myMac; // Set the MAC address of the
										// sender to our MAC add
		arp.sender_protoaddr = srcip.getAddress(); // Set the IP address of the
													// sender to our IP add
		arp.target_hardaddr = broadcast; // Set the MAC address of the
											// remote to the broadcast add
		arp.target_protoaddr = ip.getAddress(); // Set the IP address of the
												// remote to the remote IP add

		/*
		 * Initiate the EthernetPacke ether that will transport the ARP request
		 * and start to build the packet
		 */

		EthernetPacket ether = new EthernetPacket();
		ether.frametype = EthernetPacket.ETHERTYPE_ARP; // set the Ethernet
														// Frame
														// type to a ARP request
		ether.src_mac = myMac; // Set the source MAC address to our MAC address
		ether.dst_mac = broadcast; // Set the destination MAC address to the
									// broadcast ADD
		arp.datalink = ether; // Set the Data Link Layer of the arp packet
								// to the Ethernet packet

		/* send out the ARP request */

		sender.sendPacket(arp);

		/*
		 * Captor the information coming back to our computer, and see if an ARP
		 * response was sent back if not throw an Acception
		 */

		while (true) {
			ARPPacket p = (ARPPacket) captor.getPacket();
			// captor any packet that fits the filter
			if (p == null) // if the filter timesout send an acception back
			{
				throw new IllegalArgumentException(ip
						+ " did not responed to ARP request");
			}

			/*
			 * If the address of the captured packet equals the remote ip
			 * address return the senders MAC address
			 */
			System.out.println(p);
				return p.sender_hardaddr;
		}
	}
}
